/*  
  -- The Lime II Project -- 

  A tuplespaces-based middleware for coordinating agents and mobile hosts.
  Copyright (C) 2005.
  Gian Pietro Picco, Amy L. Murphy, Lorenzo Bellini.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  Lesser General Public License for more details.

  You should have received this copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 */

package lime2;

import lime2.ca.CastInfo;

/**
 * Any operation on a remote tuplespace. <br>
 * The RemoteOp class is public since it is used by package <code>ca</code>.
 * 
 * @author Gian Pietro Picco
 * @author Amy Murphy
 * @author Lorenzo Bellini
 */

public class RemoteOp extends Op implements HashID {
	// header information
	private static final long serialVersionUID = -131216975946947107L;

	/** Subtype. */
	public byte subtype = -1;

	/** The name of the involved tuplespace. */
	String name = null;

	/** The source of the message. */
	LimeServerID source = null;

	/**
	 * The target of the message. This field is not TRANSIENT because of
	 * reconciliation: if it is null then the op was multicasted.
	 */
	LimeServerID target = null;
	
	/**
	 * This field is applied when targets specified multicast is initialized.
	 * This field is TRANSIENT because <code>CastInfo</code> object will be
	 * sent along this op, which may be a sub tree of the multicast. Therefore,
	 * this field is only used for carrying the targets given by the upper users.
	 */
	transient LimeServerID[] targets = null;

	/** The source thread. */
	byte sourceT = -1;

	/** The target thread. */
	byte targetT = -1;

	/** Operation identifier. */
	long ti = -1;

	/** Sequence number. */
	int seqnum = -1;
	
	/** for mesh based multicast */
	CastInfo castInfo;

	// end of header information

	public CastInfo getCastInfo() {
		return castInfo;
	}
	
	public void setCastInfo(CastInfo castInfo) {
		this.castInfo = castInfo;
	}
	/**
	 * Result to return to the agent, if any. TRANSIENT: it is used only locally for synchronizing on a result.
	 */
	transient Result result;

	/**
	 * Private constructor. Creates a shallow copy of a remote op.
	 * 
	 * @param ro
	 *            remote op to copy
	 */
	private RemoteOp(RemoteOp ro) {
		super(ro.type);
		this.subtype = ro.subtype;
		this.name = ro.name;
		this.sourceT = ro.sourceT;
		this.targetT = ro.targetT;
		this.source = ro.source;
		this.target = ro.target;
		this.ti = ro.ti;
		this.seqnum = ro.seqnum;
	}

	/**
	 * Constructor method. Creates a new (empty) message.
	 * 
	 * @param type
	 *            of the op
	 * @param name
	 *            of the involved tuplespace
	 */
	public RemoteOp(byte type, String name) {
		super(type);
		this.name = name;
	}

	/**
	 * Fills the (header of) this empty message. Must be invoked by a thread of
	 * the pool.
	 * 
	 * @param subtype
	 *            of the message
	 */
	public void set(byte subtype) {
		this.subtype = subtype;
		this.source = Location.HERE.getID();
		this.sourceT = CommunicationMgr.get().getThreadID();
		this.ti = CommunicationMgr.get().getTI();
		this.seqnum = CommunicationMgr.get().getSeqnum();
	}

	/**
	 * Changes this message into a REPLY message: please note that the subclass
	 * of the remote op is preserved. Must be invoked by a thread of the pool.
	 * 
	 * @return this message
	 */
	public RemoteOp reply() {
		replyHeader(LimeConstants.REPLY);
		return this;
	}

	/**
	 * Changes this message into a REJECT message.
	 */
	public void reject() {
		this.subtype = LimeConstants.REJECT;
	}

	/**
	 * Checks whether the given remote ops are request/reply: we test ID of the
	 * replying host, sequence number and operation identifier. For unicast
	 * communication only; multicast communication uses the tuplespace name, not
	 * the target field.
	 * 
	 * @param request
	 *            remote op
	 * @param reply
	 *            remote op
	 * @return <code>true</code> or <code>false</code>
	 */
	public static boolean match(RemoteOp request, RemoteOp reply) {
		// "fastest" checks are performed at the beginning of the
		// short-circuited expression
		return (request.ti == reply.ti) && (request.seqnum + 1 == reply.seqnum)
		&& (request.target.equals(reply.source));
	}

	/**
	 * Checks whether the given remote ops are duplicates. Useful for multicast
	 * communication.
	 * 
	 * @param o1
	 *            remote op
	 * @param o2
	 *            remote op
	 * @return <code>true</code> or <code>false</code>
	 */
	public static boolean duplicates(RemoteOp o1, RemoteOp o2) {
		// "fastest" checks are performed at the beginning of the
		// short-circuited expression
		return (o1.ti == o2.ti) && (o1.seqnum == o2.seqnum)
		&& (o1.source.equals(o2.source));
	}

	/**
	 * Creates a new ACK message for the given remote op. Must be invoked by a
	 * thread of the pool.
	 * 
	 * @param ro
	 *            given remote op
	 * @return ACK message
	 */
	public static RemoteOp ack(RemoteOp ro) {
		RemoteOp ret = new RemoteOp(ro);
		ret.replyHeader(LimeConstants.ACK);
		return ret;
	}

	/**
	 * Creates a new FIN message for the given remote op. Must be invoked by a
	 * thread of the pool.
	 * 
	 * @param ro
	 *            given remote op
	 * @return FIN message
	 */
	public static RemoteOp fin(RemoteOp ro) {
		RemoteOp ret = new RemoteOp(ro);
		ret.replyHeader(LimeConstants.FIN);
		return ret;
	}

	// changes this message into a reply. Must be invoked by a thread of the
	// pool
	private void replyHeader(byte subtype) {
		this.subtype = subtype;
		this.target = this.source;
		this.source = Location.HERE.getID();
		this.targetT = this.sourceT;
		this.sourceT = CommunicationMgr.get().getThreadID();
		this.seqnum++;
	}

	/**
	 * Tests for equality.
	 * 
	 * @param o
	 *            Object
	 * @return <code>true</code> if the objects match, <code>false</code>
	 *         otherwise
	 */
	public boolean equals(Object o) {
		boolean result = false;
		if ((o != null) && (o instanceof RemoteOp))
			result = RemoteOp.duplicates(this, (RemoteOp) o);
		return result;
	}

	/**
	 * Returns the hashcode.
	 * 
	 * @return hashcode
	 */
	//	public int hashCode() {
	//		return ((source == null ? new Integer(0) : new Integer(source
	//				.hashCode())).toString()
	//				+ new Long(ti).toString() + new Integer(seqnum).toString())
	//				.hashCode();
	//	}

	/**
	 * Returns the source (as a String) of this remote op; invoked only by
	 * MsgTestCA.
	 * 
	 * @return textual representation of the source of this remote op
	 */
	public String getSourceString() {
		return source.toString();
	}

	/**
	 * toString method.
	 * 
	 * @return textual representation
	 */
	public String toString() {
		return "RemoteOp (" + Translator.translateType(type) + ", "
		+ Translator.translateSubtype(subtype) + ")";
	}

	public Result getResult() {
		return result;
	}
}